﻿Imports Microsoft.VisualBasic
Imports Microsoft.VisualBasic.CompilerServices
Imports System
Imports System.Diagnostics
Imports System.Net
Imports System.Net.Sockets
Imports System.Runtime.CompilerServices

Public Class SocketConnector
    ' Events
    Public Custom Event Closed As ClosedEventHandler
        AddHandler(ByVal value As ClosedEventHandler)

        End AddHandler

        RemoveHandler(ByVal value As ClosedEventHandler)

        End RemoveHandler

        RaiseEvent()

        End RaiseEvent
    End Event
    Public Custom Event Connected As ConnectedEventHandler
        AddHandler(ByVal value As ConnectedEventHandler)

        End AddHandler

        RemoveHandler(ByVal value As ConnectedEventHandler)

        End RemoveHandler

        RaiseEvent()

        End RaiseEvent
    End Event
    Public Custom Event ConnectFail As ConnectFailEventHandler
        AddHandler(ByVal value As ConnectFailEventHandler)

        End AddHandler

        RemoveHandler(ByVal value As ConnectFailEventHandler)

        End RemoveHandler

        RaiseEvent()

        End RaiseEvent
    End Event
    Public Custom Event DataArrival As DataArrivalEventHandler
        AddHandler(ByVal value As DataArrivalEventHandler)

        End AddHandler

        RemoveHandler(ByVal value As DataArrivalEventHandler)

        End RemoveHandler

        RaiseEvent()

        End RaiseEvent
    End Event

    ' Methods
    Public Sub Close()
        Me.CloseSocket(False)
    End Sub

    Private Sub CloseSocket(ByVal DoEvent As Boolean)
        If Me.SockRunning Then
            Me.SockRunning = False
            If Me.SockConnected Then
                Me.SockConnected = False
                Me.Sock.Shutdown(SocketShutdown.Both)
            End If
            Me.Sock.Close()
        End If
        If DoEvent Then
            Dim closedEvent As ClosedEventHandler = Me.ClosedEvent
            If (Not closedEvent Is Nothing) Then
                closedEvent.Invoke()
            End If
        End If
    End Sub

    Public Sub Connect(ByVal IP As String, ByVal SocksIP As String, ByVal SocksVer As Integer, ByVal SocksEnabled As Boolean)
        If Not Me.SockRunning Then
            Dim str As String
            If SocksEnabled Then
                str = SocksIP
                Me.ServerIP = IP
                Me.SocksAuthenticated = False
                Me.SocksVersion = SocksVer
            Else
                str = IP
                Me.SocksAuthenticated = True
            End If
            Dim strArray2 As String() = Strings.Split(str, ":", -1, CompareMethod.Binary)
            Dim strArray As String() = Strings.Split(strArray2(0), ".", -1, CompareMethod.Binary)
            Dim address As Byte() = New Byte() {CByte(Conversions.ToInteger(strArray(0))), CByte(Conversions.ToInteger(strArray(1))), CByte(Conversions.ToInteger(strArray(2))), CByte(Conversions.ToInteger(strArray(3)))}
            Dim remoteEP As New IPEndPoint(New IPAddress(address), Conversions.ToInteger(strArray2(1)))
            Me.Sock = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            Me.SockRunning = True
            Me.Sock.BeginConnect(remoteEP, New AsyncCallback(AddressOf Me.ConnectCallback), Nothing)
        End If
    End Sub

    Private Sub ConnectCallback(ByVal ar As IAsyncResult)
        If Me.SockRunning Then
            Try
                Me.Sock.EndConnect(ar)
                Me.SockConnected = True
                If Me.SocksAuthenticated Then
                    Dim connectedEvent As ConnectedEventHandler = Me.ConnectedEvent
                    If (Not connectedEvent Is Nothing) Then
                        connectedEvent.Invoke()
                    End If
                Else
                    Me.SocksConnect()
                End If
                Me.ReceiveData()
            Catch exception1 As Exception
                ProjectData.SetProjectError(exception1)
                Dim exception As Exception = exception1
                Me.ConnectionFailed()
                ProjectData.ClearProjectError()
            End Try
        End If
    End Sub

    Private Sub ConnectionFailed()
        If Me.SockRunning Then
            Me.SockRunning = False
            If Me.SockConnected Then
                Me.SockConnected = False
                Me.Sock.Shutdown(SocketShutdown.Both)
            End If
            Me.Sock.Close()
        End If
        Dim connectFailEvent As ConnectFailEventHandler = Me.ConnectFailEvent
        If (Not connectFailEvent Is Nothing) Then
            connectFailEvent.Invoke()
        End If
    End Sub

    Private Function GetIPBytes(ByVal IP As String, ByVal SocksOrder As Integer) As String
        Dim strArray2 As String() = Strings.Split(IP, ":", -1, CompareMethod.Binary)
        Dim strArray As String() = Strings.Split(strArray2(0), ".", -1, CompareMethod.Binary)
        Dim num2 As Long = Conversions.ToLong(strArray2(1))
        Dim a As Double = Conversion.Fix(CDbl((CDbl(num2) / 256)))
        num2 = CLng(Math.Round(CDbl((num2 - (256 * a)))))
        Dim str2 As String = (Conversions.ToString(Strings.Chr(Conversions.ToInteger(strArray(0)))) & Conversions.ToString(Strings.Chr(Conversions.ToInteger(strArray(1)))) & Conversions.ToString(Strings.Chr(Conversions.ToInteger(strArray(2)))) & Conversions.ToString(Strings.Chr(Conversions.ToInteger(strArray(3)))))
        Dim str3 As String = (Conversions.ToString(Strings.Chr(CInt(Math.Round(a)))) & Conversions.ToString(Strings.Chr(CInt(num2))))
        If (SocksOrder = 4) Then
            Return (str3 & str2)
        End If
        Return (str2 & str3)
    End Function

    Private Sub HandleData(ByVal Data As String)
        If Me.SocksAuthenticated Then
            Dim dataArrivalEvent As DataArrivalEventHandler = Me.DataArrivalEvent
            If (Not dataArrivalEvent Is Nothing) Then
                dataArrivalEvent.Invoke(Data)
            End If
        Else
            Dim connectedEvent As ConnectedEventHandler
            If (Me.SocksVersion = 4) Then
                If (Strings.Asc(Strings.Mid(Data, 2, 1)) = 90) Then
                    Me.SocksAuthenticated = True
                    connectedEvent = Me.ConnectedEvent
                    If (Not connectedEvent Is Nothing) Then
                        connectedEvent.Invoke()
                    End If
                Else
                    Me.ConnectionFailed()
                End If
            ElseIf (Data.Length = 2) Then
                If (Strings.Mid(Data, 1, 2) = ChrW(5) & ChrW(0)) Then
                    Me.SendData((ChrW(5) & ChrW(1) & ChrW(0) & ChrW(1) & Me.GetIPBytes(Me.ServerIP, 5)))
                Else
                    Me.ConnectionFailed()
                End If
            ElseIf (Strings.Mid(Data, 1, 3) = ChrW(5) & ChrW(0) & ChrW(0)) Then
                Me.SocksAuthenticated = True
                connectedEvent = Me.ConnectedEvent
                If (Not connectedEvent Is Nothing) Then
                    connectedEvent.Invoke()
                End If
            Else
                Me.ConnectionFailed()
            End If
        End If
    End Sub

    Private Sub ReceiveCallback(ByVal ar As IAsyncResult)
        If Me.SockRunning Then
            Try
                Dim count As Integer = Me.Sock.EndReceive(ar)
                If (count = 0) Then
                    Me.CloseSocket(True)
                Else
                    Me.HandleData(Globals.Encoder.GetString(Me.Buffer, 0, count))
                    Me.ReceiveData()
                End If
            Catch exception1 As Exception
                ProjectData.SetProjectError(exception1)
                Dim exception As Exception = exception1
                Me.CloseSocket(True)
                ProjectData.ClearProjectError()
            End Try
        End If
    End Sub

    Private Sub ReceiveData()
        Me.Sock.BeginReceive(Me.Buffer, 0, Me.Buffer.Length, SocketFlags.None, New AsyncCallback(AddressOf Me.ReceiveCallback), Nothing)
    End Sub

    Public Sub SendData(ByVal Data As String)
        If (Me.SockRunning And Me.SockConnected) Then
            Me.Sock.Send(Globals.Encoder.GetBytes(Data))
        End If
    End Sub

    Private Sub SocksConnect()
        If (Me.SocksVersion = 4) Then
            Me.SendData((ChrW(4) & ChrW(1) & Me.GetIPBytes(Me.ServerIP, 4) & "Owner" & ChrW(0)))
        Else
            Me.SendData(ChrW(5) & ChrW(1) & ChrW(0))
        End If
    End Sub


    ' Fields
    Private Buffer As Byte() = New Byte(&H2801 - 1) {}
    Private LocalIP As IPAddress = Dns.Resolve(Dns.GetHostName).AddressList(0)
    Private ServerIP As String
    Private Sock As Socket
    Private SockConnected As Boolean = False
    Private SockRunning As Boolean = False
    Private SocksAuthenticated As Boolean
    Private SocksVersion As Integer

    ' Nested Types
    Public Delegate Sub ClosedEventHandler()

    Public Delegate Sub ConnectedEventHandler()

    Public Delegate Sub ConnectFailEventHandler()

    Public Delegate Sub DataArrivalEventHandler(ByVal Data As String)

    Private Function ClosedEvent() As ClosedEventHandler
        Throw New NotImplementedException
    End Function

    Private Function ConnectedEvent() As ConnectedEventHandler
        Throw New NotImplementedException
    End Function

    Private Function ConnectFailEvent() As ConnectFailEventHandler
        Throw New NotImplementedException
    End Function

    Private Function DataArrivalEvent() As DataArrivalEventHandler
        Throw New NotImplementedException
    End Function

End Class